Where's the Template?

Contents|Index|Previous|Next
Wheres the template?

C++ templates are the first language feature to require more intelligence from the environment than one usually finds on a UNIX system. Somehow the compiler and linker have to make sure that each template instance occurs exactly once in the executable if it is needed, and not at all otherwise.

There are two basic approaches to this problem, which we will refer to as the Borland model and the Cfront model.

Borland model

Borland C++ solved the template instantiation problem by adding the code equivalent of common blocks to their linker; template instances are emitted in each translation unit that uses them, and they are collapsed together at run time. The advantage of this model is that the linker only has to consider the object files themselves; there is no external complexity to worry about. This disadvantage is that compilation time is increased because the template code is being compiled repeatedly. Code written for this model tends to include definitions of all member templates in the header file, since they must be seen to be compiled.

Cfront model

The AT&T C++ translator, Cfront, solved the template instantiation problem by creating the notion of a template repository, an automatically maintained place where template instances are stored. As individual object files are built, notes are placed in the repository to record where templates and potential type arguments were seen so that the subsequent instantiation step knows where to find them. At link time, any needed instances are generated and linked in. The advantages of this model are more optimal compilation speed and the ability to use the system linker; to implement the Borland model a compiler vendor also needs to replace the linker. The disadvantages are vastly increased complexity, and thus potential for error; theoretically, this should be just as transparent, but in practice it has been very difficult to build multiple programs in one directory and one program in multiple directories using Cfront. Code written for this model tends to separate definitions of non-inline member templates into a separate file, which is magically found by the link preprocessor when a template needs to be instantiated.
Currently, G++ implements neither automatic model. In the mean time, you have several options for dealing with template instantiations.

- Compile your template-using code with -frepo. The compiler will generate files with the extension .rpo listing all of the template instantiations used in the corresponding object files which could be instantiated there; the link wrapper, collect2, will then update the .rpo files to tell the compiler where to place those instantiations and rebuild any affected object files. The link-time overhead is negligible after the first pass, as the compiler will continue to place the instantiations in the same files.
This is your best option for application code written for the Borland model, as it will just work. Code written for the Cfront model will need to be modified so that the template definitions are available at one or more points of instantiation; usually this is as simple as adding #include <tmethods.cc> to the end of each template header.
For library code, if you want the library to provide all of the template instantiations it needs, just try to link all of its object files together; the link will fail, but cause the instantiations to be generated as a side effect. Be warned, however, that this may cause conflicts if multiple libraries try to provide the same instantiations. For greater control, use explicit instantiation as described in the next option.
- Compile your code with -fno-implicit-templates to disable the implicit generation of template instances, and explicitly instantiate all the ones you use. This approach requires more knowledge of exactly which instances you need than do the others, but its less mysterious and allows greater control. You can scatter the explicit instantiations throughout your program, perhaps putting them in the translation units where the instances are used or the translation units that define the templates themselves; you can put all of the explicit instantiations you need into one big file; or you can create small files for each of the instances you need, like the following examples define, and create a template instantiation library from those files.

    #include "Foo.h"
    #include "Foo.cc"

    template class Foo<int>;
    template ostream& operator <<
                     (ostream&, const Foo<int>&);

If you are using Cfront-model code, you can probably get away with not using -fno-implicit-templates when compiling files that dont #include the member template definitions. If you use one big file to do the instantiations, you may want to compile it without -fno-implicit-templates so you get all of the instances required by your explicit instantiations (but not by any other files) without having to specify them as well.
G++ has extended the template instantiation syntax outlined in the Working Paper to allow forward declaration of explicit instantiations, explicit instantiation of members of template classes and instantiation of the compiler support data for a template class (i.e., the vtable) without instantiating any of its members as the following example shows.

    extern template int max (int, int);
    template void Foo<int>::f ();
    inline template class Foo<int>;

- Do nothing. Pretend G++ does implement automatic instantiation management. Code written for the Borland model will work fine, but each translation unit will contain instances of each of the templates it uses. In a large program, this can lead to an unacceptable amount of code duplication.
- Add #pragma interface to all files containing template definitions. For each of these files, add #pragma implementation "filename" to the top of some .C file which #includes it. Then compile everything with-fexternal-templates. The templates will then only be expanded in the translation unit which implements them (i.e., the translation unit has a #pragma implementation line for the file where they live); all other files will use external references. If youre lucky, everything should work properly. If you get undefined symbol errors, you need to make sure that each template instance which is used in the program is used in the file which implements that template. If you dont have any use for a particular instance in that file, you can just instantiate it explicitly, using the syntax from the C++ working paper.

    template class A<int>;
    template ostream& operator << (ostream&, const A<int>&);

This strategy will work with code written for either model. If you are using code written for the Cfront model, the file containing a class template and the file containing its member templates should be implemented in the same translation unit.
A slight variation on this approach is to use the flag -falt-external-templates instead; this flag causes template instances to be emitted in the translation unit that implements the header where they are first instantiated, rather than the one which implements the file where the templates are defined. This header must be the same in all translation units, or things are likely to break.
See Declarations and definitions in one header for more discussion of these pragmas.

Top|Contents|Index|Previous|Next